using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using Deepcode.Flux.Core.Services.Configuration;

namespace Deepcode.Flux.Core.Data
{
	/// <summary>
	/// Implements the base code for a manager class. Manager classes are responsible for
	/// retrieving data from the database and returning strongly typed objects that
	/// represent the data. These classes also track transaction depth and connection usage
	/// to allow one connection and transaction to be continued during nested calls.
	/// </summary>
	public class BaseManager
	{
		#region Fields
		// Protected field indicating the current connection
		protected SqlConnection ThisConnection = null;
		// True/False whether the connection was established here or not
		protected bool bEstablishedConnection = false;
		// Protected field indicating the current transaction
		protected SqlTransaction ThisTransaction = null;
		// True/False whether the transaction was established here or not
		protected bool bStartedTransaction = false;
		protected int iConnectionCount = 0;
		protected int iTransactionCount = 0;
		#endregion
		#region Construction
		/// <summary>
		/// Default constructor with no connection or transaction. Connections and
		/// Transactions will be established by this class on demand.
		/// </summary>
		public BaseManager()
		{
		}
		/// <summary>
		/// Constructor specifying the connection object.
		/// </summary>
		/// <param name="CurrentConnection"></param>
		public BaseManager(SqlConnection CurrentConnection)
		{
			ThisConnection = CurrentConnection;
		}
		/// <summary>
		/// Constructor specifyint the connection object and the current transaction
		/// </summary>
		/// <param name="CurrentConnection"></param>
		/// <param name="CurrentTransaction"></param>
		public BaseManager(SqlConnection CurrentConnection, SqlTransaction CurrentTransaction)
		{
			ThisConnection = CurrentConnection;
			ThisTransaction = CurrentTransaction;
		}
		#endregion
		#region Connection management
		/// <summary>
		/// Use this method to establish a connection to the database. If the manager was created with a connection
		/// then this will do nothing. If however, the manager needs to establish the connection itself, that is
		/// done here, and the manager is told that it established the connection so that it can be closed later.
		/// </summary>
		protected void EstablishConnection()
		{
			if (ThisConnection == null)
			{
				ThisConnection = new SqlConnection(FluxConfig.MainDSN);
				bEstablishedConnection = true;
			}

			// Ensure the connection is opened...
			if (!IsConnected) ThisConnection.Open();
			// Increment the connection counter
			iConnectionCount++;
		}
		/// <summary>
		/// Use this method when you have finished with the database connection. If the manager was
		/// created with a connection, then this essentially does nothing. However, if the manager needed
		/// to establish a connection itself, then this method will ensure that the connection is
		/// closed and disposed appropriately.
		/// </summary>
		protected void ReleaseConnection()
		{
			iConnectionCount--;
			if (bEstablishedConnection)
				if (ThisConnection != null)
				{
					if (iConnectionCount > 0) return;
					ThisConnection.Close();
					ThisConnection.Dispose();
					ThisConnection = null;
				}
		}
		#endregion
		#region Transaction Management
		/// <summary>
		/// Use this method to begin a transaction. If the manager needs to create and begin a
		/// transaction itself, then this method will establish that. If however, the object
		/// was created with a transaction that was already running, this method does nothing.
		/// </summary>
		protected void BeginTransaction()
		{
			if (ThisTransaction == null)
			{
				ThisTransaction = ThisConnection.BeginTransaction();
				bStartedTransaction = true;
			}
			// Increment the BeginTransaction counter. This is decremented when EndTransaction is called.
			// and the transaction is only ended by the original transaction starter.
			iTransactionCount++;
		}
		/// <summary>
		/// Use this method to terminate a transaction. If the manager needs to create and begin
		/// a transaction itself, then this method will end the transaction either with a commit
		/// or rollback operation. If however the object was created with a transaction that was
		/// already running, this method does nothing.
		/// </summary>
		/// <param name="mode"></param>
		protected void EndTransaction(EndTransactionMode mode)
		{
			iTransactionCount--;
			if (bStartedTransaction)
				if (ThisTransaction != null)
				{
					if (iTransactionCount > 0) return;
					if (mode == EndTransactionMode.COMMIT) ThisTransaction.Commit();
					else ThisTransaction.Rollback();

					ThisTransaction.Dispose();
					ThisTransaction = null;
				}
		}
		#endregion
		#region Properties
		/// <summary>
		/// Returns true or false if this connection is connected
		/// </summary>
		protected bool IsConnected
		{
			get
			{
				if (ThisConnection == null) return false;
				if (ThisConnection.State == System.Data.ConnectionState.Broken) return false;
				if (ThisConnection.State == System.Data.ConnectionState.Closed) return false;
				if (ThisConnection.State == System.Data.ConnectionState.Connecting) return false;
				if (ThisConnection.State == System.Data.ConnectionState.Executing) return true;
				if (ThisConnection.State == System.Data.ConnectionState.Fetching) return true;
				if (ThisConnection.State == System.Data.ConnectionState.Open) return true;
				return false;
			}
		}
		#endregion
		#region Command / Data Adapter creator utilities
		/// <summary>
		/// Creates an SqlCommand object against the current connection / transaction
		/// </summary>
		/// <param name="StoredProcedureText"></param>
		/// <returns></returns>
		protected SqlCommand CreateSqlCommand(string SQLCommandText)
		{
			SqlCommand cmd = null;
			if (ThisTransaction != null)
				cmd = new SqlCommand(SQLCommandText, ThisConnection, ThisTransaction);
			else
				cmd = new SqlCommand(SQLCommandText, ThisConnection);

			cmd.CommandType = CommandType.StoredProcedure;
			return cmd;
		}
		/// <summary>
		/// Creates an SqlDataAdapter against the current connection / transaction
		/// </summary>
		/// <param name="StoredProcedureText"></param>
		/// <returns></returns>
		protected SqlDataAdapter CreateDataAdapter(string SQLCommandText)
		{
			SqlDataAdapter da = new SqlDataAdapter(SQLCommandText, ThisConnection);
			da.SelectCommand.CommandType = CommandType.StoredProcedure;
			if (ThisTransaction != null) da.SelectCommand.Transaction = ThisTransaction;
			return da;
		}
		#endregion
	}
}
